package com.gtan.hogpen
import akka.actor.{Props, ActorLogging, ActorRef, Actor}
import akka.pattern.ask
import scala.collection.mutable
import java.sql.Connection
import java.util
import com.gtan.hogpen.ConnectionFactory.CreateConnection
import scala.concurrent.{Await, ExecutionContext}
import akka.util.Timeout
import akka.event.{LoggingReceive, Logging}
import scala.util.{Failure, Success, Try}

object HogpenPool {
  case object PRESENT
  case object Get
  case class Close(connection: Connection)
}

class HogpenPool(val maxActive: Int) extends Actor with AkkaConfig with ActorLogging {

  import HogpenPool._
  import concurrent.duration._
  import ExecutionContext.Implicits.global

  implicit val timeout = Timeout(5 seconds)
  val factory = context.actorOf(Props[ConnectionFactory], "ConnectionFactory")
  val busy = new util.IdentityHashMap[Connection, Object]()
  val waitingList = mutable.Queue.empty[ActorRef]
  val available = mutable.ArrayStack[Connection]()
  var busyCount = 0

  override def receive: Actor.Receive = LoggingReceive {
    case Get =>
      payDebt()
      if (available.nonEmpty) {
        val top = available.pop()
        busy.put(top, PRESENT)
        busyCount = busyCount + 1
        sender ! (top, context.self)
      } else {
        if (busyCount + 1 < maxActive) {
          busyCount = busyCount + 1
          factory ! CreateConnection
        }
        waitingList.enqueue(sender)
      }
    case Close(connection) =>
      busy.remove(connection)
      busyCount = busyCount - 1
      available.push(connection)
      payDebt()
      sender ! true
    case conn: Connection =>
      available.push(conn)
      payDebt()
  }

  def payDebt() = {
    var i = 0
    while (i < math.min(available.size, waitingList.size)) {
      val top = available.pop()
      busy.put(top, PRESENT)
      busyCount = busyCount + 1
      waitingList.dequeue  ! (top, context.self)
      i = i + 1
    }
  }

}
